package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.NoStockException;
import com.atguigu.common.to.MemberEntityVo;
import com.atguigu.common.to.SkuHasStockVO;
import com.atguigu.common.to.mq.OrderTo;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.order.constant.OrderConstant;
import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.dao.OrderItemDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.ProductFeignService;
import com.atguigu.gulimall.order.feign.WareFeignService;
import com.atguigu.gulimall.order.interceptor.LoginUserInterceptor;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.service.OrderService;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    MemberFeignService memberFeignService;
    @Autowired
    CartFeignService cartFeignService;
    @Autowired
    WareFeignService wareFeignService;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    ProductFeignService productFeignService;
    @Autowired
    OrderItemService orderItemService;
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    ThreadPoolExecutor executor;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberEntityVo memberEntityVo = LoginUserInterceptor.threadLocal.get();
        // 设置用户积分
        orderConfirmVo.setIntegration(memberEntityVo.getIntegration());


        // feign 在远程调用之前，要构造请求，调用很多的拦截器，但是我们没有拦截器
        // 导致丢失请求头,加上拦截器，解决这个问题

        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            // 异步设置上下文
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // 获取收货地址列表
            List<MemberAddressVo> address = memberFeignService.getAddress(memberEntityVo.getId());
            orderConfirmVo.setAddress(address);
        }, executor);

        CompletableFuture<Void> itemFuture = CompletableFuture.runAsync(() -> {
            // 异步设置上下文
            RequestContextHolder.setRequestAttributes(requestAttributes);
            // 获取购物车列表
            List<OrderItemVo> orderItemVos = cartFeignService.currentUserCartItems();
            orderConfirmVo.setItems(orderItemVos);
        }, executor)
                .thenRun(() -> {
                    // 查询是否有库存
                    List<Long> skuIds = orderConfirmVo.getItems()
                            .stream()
                            .map(OrderItemVo::getSkuId)
                            .collect(Collectors.toList());
                    R r = wareFeignService.getSkusHasStock(skuIds);
                    List<SkuHasStockVO> data = r.getData(new TypeReference<List<SkuHasStockVO>>() {
                    });

                    if (!CollectionUtils.isEmpty(data)) {
                        Map<Long, Boolean> collect = data.stream().collect(Collectors.toMap(
                                SkuHasStockVO::getSkuId,
                                SkuHasStockVO::getHasStock
                        ));
                        orderConfirmVo.setStocks(collect);
                    }
                });

        CompletableFuture
                .allOf(addressFuture, itemFuture)
                .get();

        // 计算总额和价格 自动计算

        // todo 防重令牌
        String key = OrderConstant.USER_ORDER_TOKEN_PREFIX + memberEntityVo.getId();
        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(key, token, 30, TimeUnit.MINUTES);
        orderConfirmVo.setOrderToken(token);

        return orderConfirmVo;
    }

    @Override
    @Transactional
//    @GlobalTransactional
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        responseVo.setCode(0);
        // 获取当前登录用户
        MemberEntityVo memberEntityVo = LoginUserInterceptor.threadLocal.get();
        // 下单 验证令牌，创建订单，验证价格，锁定库存

        // 1 验证令牌，对比，删除，都i要是原子性的
        // 这段脚本，返回1成功 0失败
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        String key = OrderConstant.USER_ORDER_TOKEN_PREFIX + memberEntityVo.getId();
        Boolean execute = redisTemplate.execute(
                new DefaultRedisScript<>(script, Boolean.class),
                Collections.singletonList(key),
                orderSubmitVo.getOrderToken()
        );
        if (execute != null && execute) {
            // 创建订单
            OrderCreateTo order = createOrder(memberEntityVo);
            // 验价 可不做
            // 保存订单
            saveOrder(order);
            // 锁定库存
            // 订单号，skuid skuname num
            WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
            wareSkuLockVo.setOrderSn(order.getOrder().getOrderSn());
            List<OrderItemVo> orderItemVos = order.getItems()
                    .stream()
                    .map(item -> {
                        OrderItemVo orderItemVo = new OrderItemVo();
                        orderItemVo.setSkuId(item.getSkuId());
                        orderItemVo.setTitle(item.getSkuName());
                        orderItemVo.setCount(item.getSkuQuantity());
                        return orderItemVo;
                    }).collect(Collectors.toList());
            wareSkuLockVo.setLocks(orderItemVos);
            R r = wareFeignService.orderLockStock(wareSkuLockVo);
            if (r.getCode() == 0) {
                // 锁定成功，设置订单数据
                responseVo.setOrder(order.getOrder());
                // 模拟给用户加积分 报错
//                int i = 1 / 0;
                // todo 订单创建成功，发送消息给MQ
                rabbitTemplate.convertAndSend(
                        "order-event-exchange",
                        "order.create.order",
                        order.getOrder()
                );
            } else {
                // 库存锁定失败
                responseVo.setCode(2);
                throw new NoStockException();
            }
        } else {
            // 令牌不通过
            responseVo.setCode(1);
        }
        return responseVo;
    }

    @Override
    public OrderEntity getOrderStatusByOrderSn(String orderSn) {
        OrderEntity orderEntity = getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return orderEntity;
    }

    @Override
    public void closeOrder(OrderEntity orderEntity) {
        OrderEntity order = getById(orderEntity.getId());
        // 订单状态【0->待付款；1->待发货；2->已发货；3->已完成；4->已关闭；5->无效订单】
        if (order.getStatus() == 0) {
            // 执行关单操作
            OrderEntity update = new OrderEntity();
            update.setId(order.getId());
            update.setStatus(4);
            updateById(update);
            // 发一个消息，给MQ，避免因网络原因
            // 解锁库存，先一步查询订单状态，发现是0，把死信给确认接收了，
            // 导致关单监听之后才将订单改为已关闭，导致库存永远得不到释放
            // 此时再发一个消息，直接给库存服务，补偿一下解锁消息
            // 这样库存的自动解锁，和手动解锁配合，就可以解决这个问题
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(order, orderTo);
            // todo 保证消息 100% 发送出去
            try {
                // todo 每个消息可以做好日志记录,给数据库保存每个消息的详细信息
                // todo 定期扫描数据库，把失败的消息再发一遍
                // todo 结合 发送确认机制

                rabbitTemplate.convertAndSend(
                        "order-event-exchange",
                        "order.release.other",
                        orderTo
                );
            }catch (Exception e){
                // todo 尝试重试发送消息
            }

        }
    }

    /**
     * 保存订单数据
     *
     * @param orderTo
     */
    private void saveOrder(OrderCreateTo orderTo) {
        OrderEntity order = orderTo.getOrder();
        order.setModifyTime(new Date());
        this.save(order);

        List<OrderItemEntity> orderItems = orderTo.getItems();
        orderItemService.saveBatch(orderItems);
    }

    private OrderCreateTo createOrder(MemberEntityVo memberEntityVo) {
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        // 生成订单号
        String orderSn = IdWorker.getTimeId();
        // 构建订单
        OrderEntity order = buildOrder(orderSn, memberEntityVo);
        orderCreateTo.setOrder(order);
        // 构建订单项
        List<OrderItemEntity> orderItems = buildOrderItemList(orderSn);
        orderCreateTo.setItems(orderItems);
        orderCreateTo.setFare(BigDecimal.ZERO);
        // 计算价格
        computePrice(orderCreateTo);
        return orderCreateTo;
    }

    /**
     * 构建订单主表信息
     *
     * @param orderSn
     * @param memberEntityVo
     * @return
     */
    private OrderEntity buildOrder(String orderSn, MemberEntityVo memberEntityVo) {
        OrderEntity order = new OrderEntity();
        order.setOrderSn(orderSn);
        order.setMemberId(memberEntityVo.getId());
        // 获取用户默认收获地址
        MemberAddressVo address = null;
        List<MemberAddressVo> addressList = memberFeignService.getAddress(memberEntityVo.getId());
        for (MemberAddressVo memberAddressVo : addressList) {
            if (memberAddressVo.getDefaultStatus() == 1) {
                address = memberAddressVo;
            }
        }
        if (address != null) {
            // 运费
            order.setFreightAmount(BigDecimal.ZERO);
            // 收获信息
            order.setReceiverProvince(address.getProvince());
            order.setReceiverCity(address.getCity());
            order.setReceiverRegion(address.getRegion());
            order.setReceiverDetailAddress(address.getDetailAddress());
            order.setReceiverPostCode(address.getPostCode());
            order.setReceiverName(address.getName());
            order.setReceiverPhone(address.getPhone());
        }
        // 设置订单状态信息
        // 订单状态【0->待付款；1->待发货；2->已发货；3->已完成；4->已关闭；5->无效订单】
        order.setStatus(0);
        order.setAutoConfirmDay(7);
        return order;
    }

    /**
     * 创建订单项列表
     *
     * @param orderSn
     * @return
     */
    private List<OrderItemEntity> buildOrderItemList(String orderSn) {
        List<OrderItemEntity> orderItemList = null;
        // 获取购物车选中的商品列表
        List<OrderItemVo> orderItemVos = cartFeignService.currentUserCartItems();
        if (!CollectionUtils.isEmpty(orderItemVos)) {
            orderItemList = orderItemVos.stream().map(item -> {
                OrderItemEntity orderItemEntity = buildOrderItem(item);
                orderItemEntity.setOrderSn(orderSn);
                return orderItemEntity;
            }).collect(Collectors.toList());
        }
        return orderItemList;
    }

    /**
     * 创建订单项信息
     *
     * @param item
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo item) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        // spu
        Long skuId = item.getSkuId();
        R r = productFeignService.getSpuInfoBySkuId(skuId);
        SpuInfoVo spuInfo = r.getData(new TypeReference<SpuInfoVo>() {
        });
        orderItemEntity.setSpuId(spuInfo.getId());
        orderItemEntity.setSpuBrand(spuInfo.getBrandId().toString());
        orderItemEntity.setSpuName(spuInfo.getSpuName());
        orderItemEntity.setCategoryId(spuInfo.getCatalogId());

        // sku
        orderItemEntity.setSkuId(skuId);
        orderItemEntity.setSkuName(item.getTitle());
        orderItemEntity.setSkuPic(item.getImage());
        orderItemEntity.setSkuPrice(item.getPrice());
        orderItemEntity.setSkuQuantity(item.getCount());
        orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(item.getSkuAttr(), ";"));
        // 优惠 （忽略）
        // 积分
        orderItemEntity.setGiftGrowth(item.getPrice().intValue());
        orderItemEntity.setGiftIntegration(item.getPrice().intValue());
        // 价格
        orderItemEntity.setPromotionAmount(BigDecimal.ZERO);
        orderItemEntity.setCouponAmount(BigDecimal.ZERO);
        orderItemEntity.setIntegrationAmount(BigDecimal.ZERO);
        // 当前订单项的真实金额=总金额-所有优惠金额
        BigDecimal orign = item.getPrice().multiply(BigDecimal.valueOf(item.getCount()));
        orign = orign
                .subtract(orderItemEntity.getPromotionAmount())
                .subtract(orderItemEntity.getCouponAmount())
                .subtract(orderItemEntity.getIntegrationAmount());
        orderItemEntity.setRealAmount(orign);
        return orderItemEntity;
    }

    /**
     * 计算价格
     *
     * @param orderCreateTo
     */
    private void computePrice(OrderCreateTo orderCreateTo) {
        OrderEntity order = orderCreateTo.getOrder();
        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal totalCouponAmount = BigDecimal.ZERO;
        BigDecimal totalPromotionAmount = BigDecimal.ZERO;
        BigDecimal totalIntegrationAmount = BigDecimal.ZERO;
        for (OrderItemEntity orderItem : orderCreateTo.getItems()) {
            //总金额
            BigDecimal realAmount = orderItem.getRealAmount();
            totalAmount = totalAmount.add(realAmount);

            // 其他优惠金额
            BigDecimal couponAmount = orderItem.getCouponAmount();
            totalCouponAmount = totalCouponAmount.add(couponAmount);
            BigDecimal promotionAmount = orderItem.getPromotionAmount();
            totalPromotionAmount = totalPromotionAmount.add(promotionAmount);
            BigDecimal integrationAmount = orderItem.getIntegrationAmount();
            totalIntegrationAmount = totalIntegrationAmount.add(integrationAmount);

        }
        order.setFreightAmount(order.getFreightAmount());
        // 应付总额 = 总金额+运费
        order.setPayAmount(totalAmount.add(order.getFreightAmount()));
        order.setTotalAmount(totalAmount);
        order.setPromotionAmount(totalPromotionAmount);
        order.setCouponAmount(totalCouponAmount);
        order.setIntegrationAmount(totalIntegrationAmount);
        orderCreateTo.setPayPrice(order.getPayAmount());
    }

}